#include <jni.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include "audio-video.h"
#include "log-print.h"

extern "C" {

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>

ANativeWindow *mNativeWindow;
AVFrame *mFrame;
AVFrame *mRenderFrame;
AVPacket *mPacket;
AVCodecContext *mCodecContext;
AVFormatContext *mFormatContext;

void releaseSources(JNIEnv *env, const char *fileName, jstring videoPath);

void releaseSources(JNIEnv *env, const char *fileName, jstring videoPath) {
    ANativeWindow_release(mNativeWindow);
    av_frame_free(&mFrame);
    av_frame_free(&mRenderFrame);
    av_packet_free(&mPacket);
    avcodec_close(mCodecContext);
    avcodec_free_context(&mCodecContext);
    avformat_close_input(&mFormatContext);
    avformat_free_context(mFormatContext);
    env->ReleaseStringUTFChars(videoPath, fileName);
}

typedef struct AudioTrack {
    jobject audio_track;
    jclass audio_track_class;
    jmethodID play_id;
    jmethodID stop_id;
    jmethodID write_id;
} AudioTrack;

typedef struct JavaCallBack {
    JavaVM *javaVM;
    jobject object;
    jclass this_class;
    jmethodID playing_id;
    jmethodID create_audio_track_id;
} JavaCallBack;


JavaCallBack *javaCallBack;
ANativeWindow *nativeWindow;
AudioTrack *audioTrack;
DecodecListener *listener;

/**
 * 在Java层创建AudioTrack
 * @param env
 * @param playerUtil
 * @param out_sample_rate
 * @param out_channel_nb
 */
void createAudioTrack(int32_t out_sample_rate, int32_t out_channel_nb) {
    audioTrack = (AudioTrack *) malloc(sizeof(AudioTrack));
    //AudioTrack对象
    JNIEnv *env;
    (javaCallBack->javaVM)->AttachCurrentThread(&env, nullptr);
    //调用Java层的createAudioTrack
    jobject at = env->CallObjectMethod(javaCallBack->object,
                                       javaCallBack->create_audio_track_id,
                                       out_sample_rate,
                                       out_channel_nb);
    audioTrack->audio_track = env->NewGlobalRef(at);
    //获得AudioTrack的class
    audioTrack->audio_track_class = env->GetObjectClass(audioTrack->audio_track);
    //AudioTrack.play
    audioTrack->play_id = env->GetMethodID(audioTrack->audio_track_class, "play", "()V");
    //AudioTrack.stop
    audioTrack->stop_id = env->GetMethodID(audioTrack->audio_track_class, "stop", "()V");
    //AudioTrack.write
    audioTrack->write_id = env->GetMethodID(audioTrack->audio_track_class, "write", "([BII)I");
    javaCallBack->javaVM->DetachCurrentThread();
}

void destroyAudioTrack() {
    JNIEnv *env;
    (javaCallBack->javaVM)->AttachCurrentThread(&env, nullptr);
    env->DeleteGlobalRef(audioTrack->audio_track);
    javaCallBack->javaVM->DetachCurrentThread();
    free(audioTrack);
}

/**
 *调用Java层的audioTrack.play()
 */
void audioTrackPlay() {
    if (audioTrack != nullptr) {
        JNIEnv *env;
        javaCallBack->javaVM->AttachCurrentThread(&env, nullptr);
        //调用audioTrack.play()
        env->CallVoidMethod(audioTrack->audio_track, audioTrack->play_id);
        javaCallBack->javaVM->DetachCurrentThread();
    }
}

/**
 *调用Java层的audioTrack.stop()
 */
void audioTrackStop() {
    if (audioTrack != nullptr) {
        JNIEnv *env;
        javaCallBack->javaVM->AttachCurrentThread(&env, nullptr);
        //调用audioTrack.stop()
        env->CallVoidMethod(audioTrack->audio_track, audioTrack->stop_id);
        javaCallBack->javaVM->DetachCurrentThread();
    }
}

/**
 * 调用Java层的audioTrack.write()
 * @param out_buffer
 * @param out_buffer_size
 */
void audioTrackWrite(uint8_t *out_buffer, int32_t out_buffer_size) {
    if (audioTrack != nullptr) {
        JNIEnv *env;
        javaCallBack->javaVM->AttachCurrentThread(&env, nullptr);
        //out_buffer缓冲区数据 -> Java的byte[]
        jbyteArray audio_sample_array = env->NewByteArray(out_buffer_size);
        jbyte *sample_byte_p = env->GetByteArrayElements(audio_sample_array, nullptr);
        //out_buffer的数据复制到sample_byte_p
        memcpy(sample_byte_p, out_buffer, out_buffer_size);
        env->ReleaseByteArrayElements(audio_sample_array, sample_byte_p, 0);
        //调用audioTrack.write()
        env->CallIntMethod(audioTrack->audio_track,
                           audioTrack->write_id,
                           audio_sample_array,
                           0,
                           out_buffer_size);
        //释放局部引用
        env->DeleteLocalRef(audio_sample_array);
        javaCallBack->javaVM->DetachCurrentThread();
    }
}

/**
 *调用java层playing(long time)方法
 *@param time
 */
void callTime(int64_t time) {
    //JNIEnv *env;
    //javaCallBack->javaVM->AttachCurrentThread(&env, nullptr);
    //env->CallVoidMethod(javaCallBack->thiz, javaCallBack->playing_id, time);
    //javaCallBack->javaVM->DetachCurrentThread();
}

void java_callback_init(JNIEnv *env, jobject obj) {
    env->GetJavaVM(&javaCallBack->javaVM);
    jclass clas = env->GetObjectClass(obj);
    javaCallBack->object = env->NewGlobalRef(obj);
    javaCallBack->this_class = clas;
    //javaCallBack->playing_id = env->GetMethodID(clas,"playing","(J)V");
    javaCallBack->create_audio_track_id = env->GetMethodID(clas, "createAudioTrack",
                                                         "(II)Landroid/media/AudioTrack;");
}

/**
 * 回调函数-开始解码视频
 * @return
 */
void *startDecodecVideo(void *) {
    return nullptr;
}

/**
 * 回调函数-解码一帧视频数据
 * @param buf 一帧数据RGBA
 * @param size buf的长度
 * @param width 宽度
 * @param height 高度
 * @param time 当前帧的时间，单位s
 * @return
 */
void *onDecodecVideo(uint8_t *buf, uint32_t size, uint32_t width, uint32_t height, int64_t time) {
    ANativeWindow_Buffer outBuffer;
    ANativeWindow_setBuffersGeometry(nativeWindow, width, height, WINDOW_FORMAT_RGBA_8888);
    ANativeWindow_lock(nativeWindow, &outBuffer, nullptr);
    memcpy(outBuffer.bits, buf, size);
    ANativeWindow_unlockAndPost(nativeWindow);
    callTime(time);
    return nullptr;
}

/**
 * 回调函数-视频解码结束
 * @return
 */
void *endDecodecVideo(void *) {
    ALOGD("void *endDecodecVideo(void *)");
    return nullptr;
}

/**
 * 回调函数-开始解码音频
 * @param out_sample_rate 采样率
 * @param out_channel_nb 声道个数
 * @return
 */
void *startDecodecAudio(int32_t out_sample_rate, int32_t out_channel_nb) {
    //初始化AudioTrack
    createAudioTrack(out_sample_rate, out_channel_nb);
    //启动AudioTrack
    audioTrackPlay();
    return nullptr;
}

/**
 * 回调函数-解码一帧音频数据
 * @param buf 解码后的数据
 * @param size buf长度
 * @return
 */
void *onDecodecAudio(uint8_t *buf, int32_t size) {
    //调用Java层的audioTrack.write()
    audioTrackWrite(buf, size);
    return nullptr;
}

/**
 * 回调函数-音频解码结束
 * @return
 */
void *endDecodecAudio(void *) {
    audioTrackStop();
    destroyAudioTrack();
    ALOGD("void *endDecodecAudio(void *)");
    return nullptr;
}

JNIEXPORT void JNICALL
Java_com_hengfoxwolf_ffmpegdemo_util_VideoPlayer_init(JNIEnv *env, jobject object) {
    javaCallBack = (JavaCallBack *) malloc(sizeof(JavaCallBack));
    java_callback_init(env, object);
    initFFmpeg();
    initPOSIXInfo();
}

JNIEXPORT void JNICALL
Java_com_hengfoxwolf_ffmpegdemo_util_VideoPlayer_play(JNIEnv *env, jobject object,
                                                      jstring video_path, jobject surface) {
    const char *input_path = env->GetStringUTFChars(video_path, JNI_FALSE);
    initFFmpegInfo(input_path);
    nativeWindow = ANativeWindow_fromSurface(env, surface);
    listener = (DecodecListener *) malloc(sizeof(DecodecListener));
    listener->startDecodecVideo = startDecodecVideo;
    listener->onDecodecVideo = onDecodecVideo;
    listener->endDecodecVideo = endDecodecVideo;
    listener->startDecodecAudio = startDecodecAudio;
    listener->onDecodecAudio = onDecodecAudio;
    listener->endDecodecAudio = endDecodecAudio;
    read();
    setDecodecListener(listener);
    startDecodec();
}

JNIEXPORT void JNICALL
Java_com_hengfoxwolf_ffmpegdemo_util_VideoPlayer_playPause(JNIEnv *env, jobject object,
                                                           jboolean play_pause) {
    if (play_pause) {
        startDecodec();
    } else {
        stopDecodec();
    }
}

JNIEXPORT void JNICALL
Java_com_hengfoxwolf_ffmpegdemo_util_VideoPlayer_destroy(JNIEnv *env, jobject object) {
    stopDecodec();
    env->DeleteGlobalRef(javaCallBack->object);
    free(javaCallBack);
    javaCallBack = nullptr;
    destroyFFmpegInfo();
    destroyPOSIXInfo();
}

}